// 全局watcher队列
const queue = [];
// 标识当前的watcher队列是否正在被刷新
let flushing = false;
// 表示callbacks数组中是否已经存在一个刷新的watcher队列的函数
let waiting = false;
// 存放刷新 watcher 队列的函数,或者用户调用 nextTick 方法时的回调函数
const callbacks = [];
// 表示当前浏览器任务队列中是否已经存在callback数组的函数了
let penging = false;

export default function queueWatcher(watcher) {
  // debugger;
  // 如果队列中已经存在当前watcher 就不需要重复添加
  if (queue.includes(watcher)) return;

  // watcher不存在 并且当前队列不在刷新状态 就入队
  // 因为在刷新中入队会影响watcher执行的顺序
  if (!flushing) {
    // 直接入队
    queue.push(watcher);
  } else {
    // 当前 watcher 队列正在被刷新
    // 当你的 watcher 回调函数存在更改响应式数据的情况时
    // 这个情况下就会出现刷新watcher队列时,进来新的wather
    // 但是因为队列是有序的,所以要将这个watcher插入到合适的地方,保证队列的有序进行

    // 例如
    // 刷新中的watcher队列UID为 [3,4,5,7,9]
    // 现在因为用户的nextTick回调 回调了一个 watcher 8
    // 我们要插入到指定的位置也就是 [3,4,5,7,watcher8,9]
    let flag = false;
    for (let i = queue.length - 1; i >= 0; i--) {
      if (queue[i]._Uid < watcher._Uid) {
        // 找到了要插入的位置 也就是 queue[i]的后面
        queue.splice(i + 1, 0, watcher);
        flag = true;
        break;
      }
    }

    // 如果循环完毕没找到比自己小的 那自己就是最小的
    // 直接插入到头部即可
    if (!flag) {
      queue.unshift(watcher);
    }
  }

  if (!waiting) {
    // 保证callbacks数组中只有一个刷新watcher队列的函数
    waiting = true;
    nextTick(flushSchedulerQueue);
  }
}

// 负责刷新watcher队列函数 由flushCallbacks 函数调用
function flushSchedulerQueue() {
  // flushing true 表示正在刷新
  flushing = true;
  // 给 watcher 队列 排序 根据 watcher 的 this._Uid
  queue.sort((a, b) => a._Uid - b._Uid);
  console.log("刷新 flushSchedulerQueue queue ==> ", queue);
  // 依次执行队列的 run 方法
  while (queue.length) {
    const watcher = queue.shift();
    // 执行第一个 因为要按顺序
    watcher.run();
  }
  // 走到这里 watcher 队列已经为空了 控制状态初始化
  flushing = waiting = false;
}

function nextTick(cb) {
  callbacks.push(cb);
  // 没有存在callback任务队列
  if (!penging) {
    // pengding 设置为true 表示开始刷新队列
    penging = true;
    // 放入到浏览器的异步队列
    Promise.resolve().then(flushCallbacks);
  }
}

// 负责刷新 callback数组 也就是执行callback中的每一个函数
function flushCallbacks() {
  // 表示浏览器任务队列已经被拿到执行栈执行了
  // 新的 flushCallbacks可以入栈了
  penging = false;
  while (callbacks.length) {
    const cb = callbacks.shift();
    // 执行第一个 因为要按顺序
    cb();
  }
}
